//
// Created by 顿 on 2022/10/18 0018.
//
#include <algorithm>
#include "FileCompressHuffman.h"

FileCompressHuffman::FileCompressHuffman() {
    _fileInfo.resize(256);
    for(int i=0;i<256;i++) {
        _fileInfo[i]._ch = i;
    }
}

void  FileCompressHuffman::CompressFile(const string &filePath) {
    //1.统计源文件中每个字符出现的次数
    FILE* fIn=fopen(filePath.c_str(),"rb");
    if (nullptr==fIn){
        cout<<"Compress File No Exist";
        return;
    }
    //2.读取
    uch rdBuff[1024];
    while(true){
        size_t rdSize=fread(rdBuff,1,1024,fIn);
        if(0==rdSize){
            break;
        }
        for(int i=0;i<rdSize;i++){
            _fileInfo[rdBuff[i]]._appearCount++;
        }
    }
    //3.构造huffman树
    HuffmanTree<ByteInfo>ht(_fileInfo,ByteInfo());
    //4.获取huffman编码
    GenerateHuffmanCode(ht.GetRoot());
    fseek(fIn,0,SEEK_SET);

    FILE* fout=fopen(filePath.substr(filePath.find_last_of('\\')+1).c_str(),"wb");
    //5.写用来解压缩的数据信息
    writeHeadInfo(filePath,fout);
    //6.用获取的编码对源文件改写
    uch bits=0;
    int bitCount=0;
    while(true){
        size_t  reSize=fread(rdBuff,1,1024,fIn);
        if(reSize==0){
            break;
        }
        for(size_t i=0;i<reSize;i++){
            string& strCode=_fileInfo[rdBuff[i]]._chCode;
            for(int j=0;j<strCode.size();j++){
                bits<<=1;
                if('1'==strCode[j]){
                    bits|=1;
                }
                bitCount++;
                if (bitCount==8){
                    fputc(bits,fout);
                    bits=0;
                    bitCount=0;
                }
            }
        }
    }
    //注意:最后一次bits中的8个比特位可能没有填充满
    if(bitCount>0&&bitCount<8){
        bits<<=(8-bitCount);
        fputc(bits,fout);
    }
    fclose(fIn);
    fclose(fout);
}
void FileCompressHuffman::UNCompressFile(const string &filePath) {
    //1.读取解压缩需要的信息
        FILE * fIn=fopen(filePath.c_str(),"rb");
        if(nullptr==fIn){
            cout<<"road no right"<<endl;
            return;}

        string unCompressFile=filePath.substr(filePath.find_last_of('\\')+1);
        unCompressFile=unCompressFile.substr(0,unCompressFile.find_last_of('.'));
        //a.读取文件后缀
        string strInfo;
        GetLine(fIn,strInfo);
        unCompressFile+="d."+strInfo;
        //b.读取频次信息总行数
        strInfo="";
        GetLine(fIn,strInfo);
        std::size_t lineCount=atoi(strInfo.c_str());
        for(size_t i=0;i<lineCount;i++){
            strInfo="";
            GetLine(fIn,strInfo);
            if(""==strInfo){
                strInfo+='\n';
                GetLine(fIn,strInfo);
            }
            uch ch=strInfo[0];
            _fileInfo[ch]._ch=strInfo[0];
            _fileInfo[ch]._appearCount=atoi(strInfo.c_str()+2);
        }
        //2.还原huffman树
        HuffmanTree<ByteInfo>ht(_fileInfo,ByteInfo());
        HuffmanTreeNode<ByteInfo>* cur=ht.GetRoot();
        //3.解压缩
        FILE* fOut=fopen(unCompressFile.c_str(),"wb");
        uch rdBuff[1024];
        std::size_t fileSize=0;
        while(true){
            std::size_t  rdSize=fread(rdBuff,1,1024,fIn);
            if(0==rdSize)
                break;
            for(std::size_t i=0;i<rdSize;i++){
                uch ch=rdBuff[i];
                for(int j=0;j<8;j++){
                    if(ch&0x80)
                        cur=cur->_right;
                    else
                        cur=cur->_left;
                    ch<<=1;
                    if(nullptr==cur->_left&& nullptr==cur->_right){
                        fputc(cur->_weight._ch,fOut);
                        cur=ht.GetRoot();
                        fileSize++;
                        if(fileSize==cur->_weight._appearCount)
                            break;
                    }
                }
            }
        }
        fclose(fIn);
        fclose(fOut);
}
void FileCompressHuffman::GenerateHuffmanCode(HuffmanTreeNode<ByteInfo> *root) {
    if(nullptr==root)
        return;
    GenerateHuffmanCode(root->_left);
    GenerateHuffmanCode(root->_right);
    if(nullptr==root->_left&& nullptr==root->_right){
        string& chChode=_fileInfo[root->_weight._ch]._chCode;
        HuffmanTreeNode<ByteInfo>*cur=root;
        HuffmanTreeNode<ByteInfo>*parent=root->_parent;
        while(parent){
            if(cur==parent->_left)
                chChode+='0';
            else
                chChode+='1';
            cur=parent;
            parent=cur->_parent;
        }
        reverse(chChode.begin(),chChode.end());
    }
}
void FileCompressHuffman::writeHeadInfo(const string &filePath, FILE *fOut) {
    //1获取源文件后缀
    string  headInfo;
    headInfo+=GetFilePostFix(filePath);
    headInfo+='\n';
    //2.构造频次信息
    std::size_t  appearLineCount=0;
    string chInfo;
    for(auto& e:_fileInfo){
        if(e._appearCount==0)
            continue;
        appearLineCount++;
        chInfo+=e._ch;
        chInfo+=':';
        chInfo+=to_string(e._appearCount);
        chInfo+='\n';
    }
    headInfo+=to_string(appearLineCount);
    headInfo+='\n';
    fwrite(headInfo.c_str(),1,headInfo.size(),fOut);
    fwrite(chInfo.c_str(),1,chInfo.size(),fOut);

}
string FileCompressHuffman::GetFilePostFix(const string& filePath){
    return filePath.substr(filePath.find_last_of('.')+1);
};
void FileCompressHuffman::GetLine(FILE*fIn, string& strInfo){
    while(!feof(fIn)){
        uch ch=fgetc(fIn);
        if(ch=='\n')
            break;
        strInfo+=ch;
    }
};